2019_albers_structural_constellations.py

#

SPDX-FileCopyrightText: 2019 Arthur Schweisthal SPDX-FileCopyrightText: 2024 AlICe laboratory https://alicelab.be

SPDX-License-Identifier: GPL-3.0-or-later

#

Arthur_SCHWEISTHAL Blender 2.81

import bpy
import bmesh
import random
import math
from math import radians
from math import cos
from math import sin
#
#

PARAMETERS

#

for now, the values must be equal if not, no rotation would be applied

length = 10
depth = 10
height = 10


new_thickness = 0.75
#
#

SETTINGS

verts = [
    (+depth, +length, 0),  # 0
    (+depth, 0, 0),  # 1
    (0, 0, 0),  # 2
    (0, +length, 0),  # 3
    (+depth, +length, +height),  # 4
    (+depth, 0, +height),  # 5
    (0, 0, +height),  # 6
    (0, +length, +height),
]  # 7

faces = [
    (0, 1, 2, 3),  # 0 bottom
    (4, 7, 6, 5),  # 1 top
    (0, 4, 5, 1),  # 2 front
    (1, 5, 6, 2),  # 3 left
    (2, 6, 7, 3),  # 4 back
    (4, 0, 3, 7),  # 5 right
]
#
#
                  GEOMETRY                    #
#

MAKING AN AXONOMETRIC 3D GEOMETRY

#

Creating a mesh

#
def make_mesh(verts, faces, name):
#

add new mesh in data

    mesh = bpy.data.meshes.new(name)
#

use bmesh to generate mesh

    bm = bmesh.new()
#

create bmesh vertex objects from coordinates

    for v_co in verts:
        bm.verts.new(v_co)
#

create bmesh faces from collection of previous vertexes

    bm.verts.ensure_lookup_table()
    for f_idx in faces:
        bm.faces.new([bm.verts[i] for i in f_idx])
#

put bmesh into new mesh in data

    bm.to_mesh(mesh)
    mesh.update()
#

add the mesh as an object into the scene with this utility module

    from bpy_extras import object_utils

    object_utils.object_data_add(bpy.context, mesh)
#

Making a cube

#
def make_cube(name="CUBE"):

    make_mesh(verts, faces, name)
#

Define it by its edges

#
def cube_edges(name="CUBE_EDGES"):

    bpy.ops.object.editmode_toggle()  # on

    obj = bpy.context.edit_object
    me = obj.data
    bme = bmesh.from_edit_mesh(me)
    for i in range(6):

        bme.faces.ensure_lookup_table()
#

select in face index

        bme.faces[(i)].select = True
#

use inset tool to create an inner square

        bpy.ops.mesh.inset(thickness=new_thickness)
#

extrude the inner square

        bpy.ops.mesh.extrude_region_move(
            TRANSFORM_OT_translate={
                "value": (0, 0, -new_thickness),
                "orient_type": "NORMAL",
            }
        )
#

delete the inner square to make a hole

        bpy.ops.mesh.delete(type="FACE")
#

Shear tool

#
def shear():
    bpy.ops.mesh.select_all(action="SELECT")

    bpy.ops.transform.shear(
        value=1,
        orient_axis="X",
        orient_axis_ortho="Y",
        orient_type="GLOBAL",
        orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
        orient_matrix_type="GLOBAL",
        mirror=True,
        use_proportional_edit=False,
        proportional_edit_falloff="SMOOTH",
        proportional_size=1,
        use_proportional_connected=False,
        use_proportional_projected=False,
        release_confirm=True,
    )
    bpy.ops.transform.shear(
        value=1,
        orient_axis="Y",
        orient_axis_ortho="X",
        orient_type="GLOBAL",
        orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
        orient_matrix_type="GLOBAL",
        mirror=True,
        use_proportional_edit=False,
        proportional_edit_falloff="SMOOTH",
        proportional_size=1,
        use_proportional_connected=False,
        use_proportional_projected=False,
        release_confirm=True,
    )
#

replace it as the previous tool moved the origin of the geometry

    bpy.ops.transform.translate(value=(-depth / 2, length / 2, 0))

    bpy.ops.object.editmode_toggle()  # off
#
#
              RELATIONSHIP                    #
#

RANDOMIZE RELATIONSHIP BETWEEN THE GEOMETRIES

#

Random number of 90° rotations in every axis

z = random.randint(1, 3)
y = random.randint(1, 3)
x = random.randint(1, 3)
#

is it possible to rotate it randomly if the geometry is not a cube?

if length!=depth and height!=depth and length==height:
if length==depth and height!=depth and length!=height:
if length!=depth and height==depth and length!=height:
if length!=depth and height!=depth and length!=height:

if not, only translate the cube

def rotations():
    if depth == length and length == height and depth == height:
        for i in range(z):
            bpy.ops.transform.rotate(value=1.5708, orient_axis="Z")
        for i in range(y):
            bpy.ops.transform.rotate(value=1.5708, orient_axis="Y")
        for i in range(x):
            bpy.ops.transform.rotate(value=1.5708, orient_axis="X")
#
#

HELPER: rotation data #

def printing_rotation_data():  #
#
    print(" ")  #
    print("start")  #
    print(" ")  #
    print("Number of X axis 90 degres rotations =", x)  #
    print("Number of Y axis 90 degres rotations =", y)  #
    print("Number of Z axis 90 degres rotations =", z)  #
#
#
#

Random translation of half units in every axis

r1 = random.randint(-2, 2)
r2 = random.randint(-2, 2)
r3 = random.randint(-2, 2)
#
def snap():
    for i in range(r1):
        bpy.ops.transform.translate(value=(depth / 2, 0, 0))
    for i in range(r2):
        bpy.ops.transform.translate(value=(0, length / 2, 0))
    for i in range(r3):
        bpy.ops.transform.translate(value=(0, 0, height / 2))
#

HELPER: translation data #

def printing_translation_data():  #
#
    print("Translation on X axis=", r1)  #
    print("Translation on Y axis=", r2)  #
    print("Translation on Z axis=", r3)  #
#
#
#
#
        UTILITY  AND  MAIN FONCTIONS          #
#

UTILITY FONCTIONS

#
def reset():
#

Cleaning datafile through saving and reopening

    save = bpy.ops.wm.save_mainfile
    open = bpy.ops.wm.open_mainfile
    path = bpy.data.filepath
#

test if file is previously saved

    if path is "":
        save()
        path = bpy.data.filepath
        print(".blend file saved in home directory")
#

save end reopen file

    save(filepath=path)
    open(filepath=path)
#
def deleteallobjects():
#

Delete scene before running script

    select = bpy.ops.object.select_all
    delete = bpy.ops.object.delete
#

select and delete all the objects

    select(action="SELECT")
    delete(use_global=False)
#
#

MAIN FONCTIONS

#

CREATE A FIRST GEOMETRY

def geometry():
    make_cube("Cube")
    cube_edges()
    shear()
    bpy.ops.object.origin_set(type="ORIGIN_CENTER_OF_MASS")
#

CREATE A SECOND GEOMETRY

#
def geometry2():
    geometry()
    rotations()
    snap()
#
def main_program():
#

delete all objects

    deleteallobjects()
#

create the object

    geometry()
    geometry2()
    bpy.ops.object.select_all(action="SELECT")
    bpy.ops.object.join()
#

PRINTING THE RANDOM VALUES USED

#
def printing_data():
    printing_rotation_data()

    print(" ")
    print("and")
    print(" ")

    printing_translation_data()

    print(" ")
    print("end")
    print(" ")
    print(50 * "*")
#
#
                EXECUTION                     #
main_program()

printing_data()
#
#
              REPRESENTATION                  #
#

CAMERA SETTINGS modified version of 18.11.2016 camera settings code by AlICe.lab

def PersCam(name):
    bpy.ops.object.camera_add()
    Scene = bpy.context.scene.render
    PersCam = bpy.context.object
    PersCam.data.type = "PERSP"
    PersCam.data.ortho_scale = 500
    PersCam.rotation_euler = (radians(45), 0, radians(45))
    PersCam.location = (25.0, -20.0, 35.0)
    Scene.pixel_aspect_x = 1
#
def axoCam(canon):
    bpy.ops.object.camera_add()
    Scene = bpy.context.scene.render
    AxoCam = bpy.context.object
    AxoCam.data.type = "ORTHO"
    AxoCam.data.ortho_scale = 50

    if canon == "isometrie":
        AxoCam.name = "axoIsometrie"
        AxoCam.rotation_euler = (radians(54.74), 0.0, radians(45))
        AxoCam.location = (10.0, -10.0, 10.0)
        Scene.pixel_aspect_x = 1
    if canon == "dimetrie":
        AxoCam.name = "axoDimetrie"
        AxoCam.rotation_euler = (radians(60), 0.0, radians(23))
        AxoCam.location = (5.53, -13.04, 8.18)
        Scene.pixel_aspect_x = 1
    if canon == "trimetrie":
        AxoCam.name = "axoTrimetrie"
        AxoCam.rotation_euler = (radians(67), 0.0, radians(34))
        AxoCam.location = (8.59, -12.734, 6.52)
        Scene.pixel_aspect_x = 1
#

MANUAL camera choice

#

PersCam(‘Pers1’)

axoCam("isometrie")
#

axoCam (‘dimetrie’) axoCam (‘trimetrie’)

#

AUTOMATIC camera choice To develop

#
#
#

purge the blender file

reset()